use log::error;
#[allow(dead_code)]
use std::{collections::HashMap, num::ParseIntError};
use std::{error::Error, fmt::Display};

#[derive(Debug)]
struct Expiration {
    year: u32,
    month: u32,
}
#[derive(Debug)]
struct Card {
    number: u32,
    exp: Expiration,
    cvv: u32,
}
#[derive(Debug)]
enum CreditCardError {
    InvalidInput(String),
    Other(Box<dyn Error>, String),
}

#[derive(Debug)]
struct ParsePaymentInfoError {
    source: Option<Box<dyn Error>>,
    msg: Option<String>,
}

impl From<ParseIntError> for ParsePaymentInfoError {
    fn from(err: ParseIntError) -> Self {
        Self {
            source: Some(Box::new(err)),
            msg: None,
        }
    }
}

impl std::fmt::Display for ParsePaymentInfoError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        // write!(f, "{self} \n\t {:?} ", self.msg)?;
        if let Some(e) = self.source.as_ref() {
            write!(f, "\n\t{}", e)?;
        }
        Ok(())
    }
}

impl Error for ParsePaymentInfoError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        self.source.as_deref()
    }
}

impl Display for CreditCardError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str("Credit card error")
    }
}

impl Error for CreditCardError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match self {
            CreditCardError::InvalidInput(_) => None,
            CreditCardError::Other(e, _) => Some(e.as_ref()),
        }
    }
}

fn parse_card_numbers_v2(card: &str) -> Result<Vec<u32>, ParsePaymentInfoError> {
    let numbers = card
        .split(" ")
        .into_iter()
        .map(|s| {
            s.parse().map_err(|_| ParsePaymentInfoError {
                source: None,
                msg: Some(format!("{s:?} could not be parsed as u32")),
            })
        })
        .collect::<Result<Vec<u32>, _>>()?;
    Ok(numbers)
}
fn parse_card_numbers_v1(card: &str) -> Result<Vec<u32>, ParseIntError> {
    let numbers = card
        .split(" ")
        .into_iter()
        .map(|s| s.parse())
        .collect::<Result<Vec<u32>, _>>()?;
    Ok(numbers)
}
fn parse_card_numbers(card: &str) -> Vec<u32> {
    let numbers = card
        .split(" ")
        .into_iter()
        .map(|s| s.parse())
        .collect::<Result<Vec<u32>, _>>()
        .unwrap();
    numbers
}

fn parse_card_v2(card: &str) -> Result<Card, ParsePaymentInfoError> {
    let mut numbers = parse_card_numbers_v2(card)?;
    let len = numbers.len();
    let expected_len = 4;
    if len != expected_len {
        return Err(ParsePaymentInfoError {
            source: None,
            msg: Some(format!(
                "excepted {expected_len} numbers but got {len} elements:{numbers:?}"
            )),
        });
    }
    let cvv = numbers.pop().unwrap();
    let year = numbers.pop().unwrap();
    let month = numbers.pop().unwrap();
    let number = numbers.pop().unwrap();
    Ok(Card {
        number,
        exp: Expiration { year, month },
        cvv,
    })
}

fn parse_card(card: &str) -> Result<Card, String> {
    let mut numbers = parse_card_numbers_v1(card).map_err(|e| e.to_string())?;
    let len = numbers.len();
    let expected_len = 4;
    if len != expected_len {
        return Err(format!(
            "excepted {expected_len} numbers but got {len} elements:{numbers:?}"
        ));
    }
    let cvv = numbers.pop().unwrap();
    let year = numbers.pop().unwrap();
    let month = numbers.pop().unwrap();
    let number = numbers.pop().unwrap();
    Ok(Card {
        number,
        exp: Expiration { year, month },
        cvv,
    })
}

fn parse_card_v1(card: &str) -> Result<Card, String> {
    let mut numbers = parse_card_numbers_v1(card).map_err(|e| e.to_string())?;
    let len = numbers.len();
    let expected_len = 4;
    if len != expected_len {
        return Err(format!(
            "excepted {expected_len} numbers but got {len} elements:{numbers:?}"
        ));
    }
    let cvv = numbers.pop().unwrap();
    let year = numbers.pop().unwrap();
    let month = numbers.pop().unwrap();
    let number = numbers.pop().unwrap();
    Ok(Card {
        number,
        exp: Expiration { year, month },
        cvv,
    })
}

fn get_credit_card_info(credit_cards: &HashMap<&str, &str>, name: &str) -> Result<Card, String> {
    let card_string = credit_cards.get(name).unwrap();
    let card = parse_card(card_string)?;
    Ok(card)
}
// fn get_credit_card_info_v1(
//     credit_cards: &HashMap<&str, &str>,
//     name: &str,
// ) -> Result<Card, CreditCardError> {
//     let card_string = credit_cards
//         .get(name)
//         .ok_or(CreditCardError::InvalidInput(format!(
//             "No credit card was found for {name}"
//         )))?;
//     let card = parse_card(card_string)
//         .map_err(|e| CreditCardError::Other(e, format!("{name}'s card could not be parsed")))?;
//     Ok(card)
// }

fn get_credit_card_info_v2(
    credit_cards: &HashMap<&str, &str>,
    name: &str,
) -> Result<Card, CreditCardError> {
    let card_string = credit_cards
        .get(name)
        .ok_or(CreditCardError::InvalidInput(format!(
            "No credit card was found for {name}"
        )))?;
    let card = parse_card_v2(card_string).map_err(|e| {
        CreditCardError::Other(Box::new(e), format!("{name}'s card could not be parsed"))
    })?;
    Ok(card)
}

fn main() {
    let credit_cards = HashMap::from([
        ("Amy", "1234567 04 25 123"),
        ("Bob", "1234567 04 124"),
        ("Charlie", "1234567 04 25 125"),
    ]);

    // let result = get_credit_card_info(&credit_cards, "Bob");
    // // println!("{:?}", card);
    // match result {
    //     Ok(card) => println!("{:?}", card),
    //     Err(e) => println!("{:?}", e),
    // }
    env_logger::init();
    // 名字从输入框输入
    println!("Enter name: ");

    let mut name = String::new();
    std::io::stdin()
        .read_line(&mut name)
        .expect("Failed to read line");

    let result = get_credit_card_info_v2(&credit_cards, name.trim());
    // println!("{:?}", card);
    match result {
        Ok(card) => println!("{:?}", card),
        Err(err) => {
            match &err {
                CreditCardError::InvalidInput(msg) => println!("{msg}"),
                CreditCardError::Other(e, _) => println!(
                    "
            \nSomething went wrong! Try again later! {e}"
                ),
                // CreditCardError::Other(msg, inner_msg) => println!("{msg}:{inner_msg}"),
            }
            error!("错误信息 \n{err:?}")
        }
    }
}
